import { controllKeys, baseNums, sumMap } from "./constants";
import solveSudoku from "./solveSudoku";

export class Controll {
	public num: number | null;
	public status: boolean = false;

	constructor(num: number) {
		this.num = num;
	}
}

class Utils {
	public static generateControllBar(type: string): Controll[] {
		return controllKeys[type].map((key) => new Controll(key));
	}

	public static getPalaceKey(
		type: string,
		rowKey: number,
		colKey: number
	): number {
		switch (type) {
			case "4":
				return Math.floor(rowKey / 2) * 2 + Math.floor(colKey / 2) + 1;
			case "6":
				return Math.floor(rowKey / 2) * 3 + Math.floor(colKey / 3) + 1;
			case "9":
			default:
				return Math.floor(rowKey / 3) * 3 + Math.floor(colKey / 3) + 1;
		}
	}

	public static random(start: number, end: number) {
		return Math.floor(Math.random() * (end + 1 - start) + start);
	}

	public static randomOne2Nine(type: string) {
		const nums = [...baseNums[type]];
		Utils.randomArray(type, nums);
		return nums;
	}

	public static randomArray(type: string, array: any[]) {
		const len = array.length - 1;
		Array(parseInt(type) + 1)
			.fill(0)
			.forEach(() => {
				const start = Utils.random(0, len);
				const end = Utils.random(0, len);
				[array[start], array[end]] = [array[end], array[start]];
			});
	}

	public static isCorrectData(
		type: string,
		array: Array<Array<number | null>>
	): boolean {
		const SUM = sumMap[type];
		let isCorrect = true;
		const sums: any = {};

		function collect(num: number | null, rowIdx: number, colIdx: number) {
			const rowKey = `row${rowIdx}`;
			const colKey = `col${colIdx}`;
			const palaceKey = `palace${Utils.getPalaceKey(type, rowIdx, colIdx)}`;

			if (sums[rowKey]) {
				sums[rowKey] += num;
			} else {
				sums[rowKey] = num;
			}

			if (sums[colKey]) {
				sums[colKey] += num;
			} else {
				sums[colKey] = num;
			}

			if (sums[palaceKey]) {
				sums[palaceKey] += num;
			} else {
				sums[palaceKey] = num;
			}
		}

		array.forEach((row, rowIdx) =>
			row.forEach((grid, colIdx) => collect(grid, rowIdx, colIdx))
		);

		Object.keys(sums).forEach((key) => {
			if (sums[key] !== SUM) {
				isCorrect = false;
			}
		});

		return isCorrect;
	}
	/**
	 * 检测数独是否有唯一解
	 * @param type
	 * @param array
	 * @returns
	 */
	public static checkData(
		type: string,
		array: Array<Array<number | null>>
	): boolean {
		const result = solveSudoku(type, array);
		console.log("解个数:" + result.length, result);
		if (result.length == 1) return true;
		return false;
	}
	/**
	 * 每宫都有数字的检查
	 * @param type
	 * @param array
	 * @returns
	 */
	public static checkData2(
		type: string,
		array: Array<Array<number | null>>
	): boolean {
		let isCorrect = true;
		const sums: any = {};

		function collect(num: number | null, rowIdx: number, colIdx: number) {
			const rowKey = `row${rowIdx}`;
			const colKey = `col${colIdx}`;
			const palaceKey = `palace${Utils.getPalaceKey(type, rowIdx, colIdx)}`;

			// if (sums[rowKey]) {
			// 	sums[rowKey] += num;
			// } else {
			// 	sums[rowKey] = num;
			// }

			// if (sums[colKey]) {
			// 	sums[colKey] += num;
			// } else {
			// 	sums[colKey] = num;
			// }

			if (sums[palaceKey]) {
				sums[palaceKey] += num;
			} else {
				sums[palaceKey] = num;
			}
		}

		array.forEach((row, rowIdx) =>
			row.forEach((grid, colIdx) => collect(grid, rowIdx, colIdx))
		);

		Object.keys(sums).forEach((key) => {
			if (!sums[key]) {
				isCorrect = false;
			}
		});

		return isCorrect;
	}
	public static getUrlHash() {
		return (
			window.location.hash &&
			window.location.hash.substring(
				2,
				window.location.hash.indexOf("?") > 2
					? window.location.hash.indexOf("?")
					: window.location.hash.length
			)
		);
	}
	/**
	 * ​​空格自由度（约束强度）​​
​​定义​​：指每个空格所在行、列、宫格内其他空格数量的总和（不含自身）。计算公式为：
S(i,j)=S(行)+S(列)+S(宫)−3
其中 S(行)、S(列)、S(宫) 分别为该空格所在行、列、宫内的空格总数。
​​影响​​：自由度越高，说明约束条件越少，候选数范围越大，推理难度越高。例如：
自由度范围在700–820为初级，1300以上为极高难度

​​难度等级​​	​​空格数范围​​	​​空格自由度范围​​	​​典型技巧要求​​
初级（简单）	24–34	700–820	摒除法、唯一候选数
中级（中等）	35–42	820–940	数对、基础链式推理
高级（困难）	42–52	940–1060	数组、X-Wing
专家级	52–65	1060–1300+	复杂链、唯一矩形等
	 */
	public static calcDifficulty(
		type: string,
		array: Array<Array<number | null>>
	): number {
		const numType: number = parseInt(type);
		let difficulty: number = 0;
		function collect(num: number | null, rowIdx: number, colIdx: number) {
			let result: number = 0;
			if (num == null) {
				//空格才需要计算
				//x
				for (let i = 0; i < numType; i++) {
					if (array[rowIdx][i] == null) {
						result++;
					}
				}
				//y
				for (let i = 0; i < numType; i++) {
					if (array[i][colIdx] == null) {
						result++;
					}
				}
				//宫
				let palace = Utils.getPalaceKey(type, rowIdx, colIdx);

				for (
					let x = Math.max(0, colIdx - 2);
					x < Math.min(numType, colIdx + 2);
					x++
				) {
					for (
						let y = Math.max(0, rowIdx - 2);
						y < Math.min(numType, rowIdx + 2);
						y++
					) {
						if (
							array[y][x] == null &&
							Utils.getPalaceKey(type, rowIdx, colIdx) == palace
						) {
							result++;
						}
					}
				}
				result -= 3;
				difficulty += result;
			}
		}

		array.forEach((row, rowIdx) =>
			row.forEach((grid, colIdx) => collect(grid, rowIdx, colIdx))
		);
		return difficulty;
	}
}

export default Utils;
